// Interface.cpp
// Copyright 2012 Ben Lewis
// Licensed under GPLv2
// Terms available at http://www.gnu.org/licenses/gpl-2.0.txt

#include <iostream>
#include <exception>
#include <stdexcept>
#include <iomanip>
#include <vector>
#include <complex>
#include "CImg.h"
#include "Complex_Polynomial.h"
#include "Newton_Fractal.h"

//using std

istream& read_img_dims( istream& in , int& xdim , int& ydim );
istream& read_plot_dims(istream& in , double& xmin , double& xmax , 
			double& ymin , double& ymax );
istream& read_roots(istream& in , vector< complex<double> >& roots );
istream& read_delta( istream& in , double& del );

/* Tasks for this file:
 - Take command line input
 - Assign integer values xdim, ydim
 - Assign double values xmin, xmax, ymin, ymax
 - Sanity check x/y min/max
 - Take an open ended vector of complex roots
 - Determine if colours should be random or standard
 - Assign double value delta
*/

int main() {
  Complex_Polynomial function;
  int xdim, ydim;
  double xmin, xmax, ymin, ymax, delta;
  string colours;

  //running this in a try-catch arrangement, since I've set all of these up to do so.
  try {
    cout << "Please enter image x- and y-dimensions (int), separated by a space,"
	 << "then press return" << endl;
    read_img_dims(cin, xdim, ydim);
    cout << "Please enter x minimum and maximum values (double), followed by "
	 << "y minimum and maximum values, again spaced." << endl;
    read_plot_dims(cin,xmin,xmax,ymin,ymax);
    cout << "Please enter the roots of the polynomial to be graphed, "
	 << "pairing each real and complex part with no additional "
	 << "characters." << endl;
    read_roots(cin,function.roots);
    cout << "Please enter the delta value to be used (double)."
	 << endl;
    read_delta(cin,delta);
    // Now that all the values have been properly entered
    // and sanity tests applied, it's time to run this code.
    CImg<unsigned char> fractal = plot (function, xdim, ydim,
					xmin, xmax, ymin, ymax,
					delta);
    CImgDisplay main_disp(fractal.HSVtoRGB(),"Fractal");
    while ( !main_disp.is_closed() )
      main_disp.wait();
  } catch ( domain_error e ) {
    cout << e.what() << endl;
    cout << "Please fix the error and run again." << endl;
  }
  return 0;
}

// Assign x- and y- dimensions to the output image
// Sanity check for non-zero values
istream& read_img_dims( istream& in , int& xdim , int& ydim ) {
  if ( in ) {
    if ( in >> xdim >> ydim ) {
      if ( ( xdim == 0 ) || ( ydim == 0 ) )
	// I'm going to look for a better way to handle this.
	throw domain_error("Zero dimension.");
    } else
      throw domain_error("Not enough arguments.");
    in.clear();
  }
  return in;
}

// Uses the istream I'm using everywhere to assign the range values.
istream& read_plot_dims(istream& in , double& xmin , double& xmax , 
			 double& ymin , double& ymax ) {
  if ( in ) {
    if ( in >> xmin >> xmax >> ymin >> ymax ){
      if ( ( xmin >= xmax ) || ( ymin >= ymax ) ) 
	// Can't think of a cleaner way to do this at the moment.
	throw domain_error("Bad range.");
    } else
      throw domain_error("Not all range values set.");
    in.clear();
  }
  return in;
}

// Reads in pairs of doubles and loads them into the roots vector.
istream& read_roots(istream& in , vector< complex<double> >& roots ) {
  if ( in ) {
    double x, y; // Defining Re, Im component variables.
    while ( in ) {
      in >> x;
      in >> y;
      roots.push_back( complex<double> ( x , y ) );
      if ( in.peek() == '\n' )
	break;
    }
    // This loop will only run if both are filled
    // As soon as it runs out of paired input, stops.
    in.clear();
  }
  // To remove any failure state
  return in;
}

istream& read_delta( istream& in , double& del ) {
  if ( in ) {
    if ( in >> del ) {
      if ( del == 0 )
	throw domain_error("Zero radius of convergence.");
    } else
      throw domain_error("No delta defined.");
    in.clear();
  }
  return in;
}
